﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using UserSurface;
using ApplicationCore;
using System.Threading;

namespace EnzymeLaborTool.View.Controls
{
    public partial class BatchCtrl : UserControl
    {
        private Guid _newItemUId = new Guid("11111111-1111-1111-1111-111111111111");
        private int _nextBatchNumber = 1;
        private System.Windows.Forms.Timer searchTimer = new System.Windows.Forms.Timer();
        private System.Windows.Forms.Timer navigationTimer = new System.Windows.Forms.Timer();
        private DateTime _minDate = new DateTime(1753, 1, 1);
        private DateTime _maxDate = new DateTime(9999, 12, 31);
        private MainSurface _surface;
        private EnzymeDBDataContext _dataContext;

        public string SearchTerm
        {
            get
            {
                return filterBatchesTextBox.Text;
            }
        }

        public BatchView SelectedBatch
        {
            get
            {
                return batchesBindingSource.Current as BatchView;
            }
        }

        public BatchCtrl()
        {
            InitializeComponent();
            batchesBindingNavigator.Items.Insert(10, new ToolStripControlHost(kryptonDropButton1));
            kryptonContextMenuRadioButtonSplit.CheckedChanged += new EventHandler(kryptonContextMenuRadioButtonSplit_CheckedChanged);
            kryptonContextMenuRadioButtonFull.CheckedChanged += new EventHandler(kryptonContextMenuRadioButtonFull_CheckedChanged);
            searchTimer.Tick += new EventHandler(searchTimer_Tick);
            navigationTimer.Tick += new EventHandler(navigationTimer_Tick);
            batchLayoutCtrl1.UnSavedChangesChanged += new EventHandler(bachtLayoutCtrl1_UnSavedChangesChanged);
            batchDataGridView.NavigationRejected += new EventHandler(batchDataGridView_NavigationRejected);
        }

        void navigationTimer_Tick(object sender, EventArgs e)
        {
            navigationTimer.Stop();
            LoadBatchLayout();
        }

        void batchDataGridView_NavigationRejected(object sender, EventArgs e)
        {

            if (RequestToNavigateAway(false))
                batchDataGridView.ResumeNavigation();

        }

        private bool RequestToNavigateAway(bool rejectChanges)
        {
            DialogResult dr = MessageBox.Show("You have unsaved changes in your current batch! Do you want finish your work now?\r\nIf you click 'No' then you will lose your last changes.", "Unsaved changes", MessageBoxButtons.YesNo);
            if (dr == DialogResult.No)
            {
                batchDataGridView.DisableNavigation = false;
                batchesBindingNavigator.Enabled = true;
                filterBatchesTextBox.Enabled = true;
                //reload batch fronm db and reject changes
                if (rejectChanges)
                {
                    LoadBatchLayout();
                }
                return true;
            }
            else
            {
                return false;
            }
        }

        void bachtLayoutCtrl1_UnSavedChangesChanged(object sender, EventArgs e)
        {
            if (batchLayoutCtrl1.UnSavedChanges)
            {
                batchDataGridView.DisableNavigation = true;
                batchesBindingNavigator.Enabled = false;
                filterBatchesTextBox.Enabled = false;
            }
            else
            {
                batchDataGridView.DisableNavigation = false;
                batchesBindingNavigator.Enabled = true;
                filterBatchesTextBox.Enabled = true;
            }
        }

        private bool _initialized = false;
        public void Init(MainSurface ms)
        {
            if (!_initialized)
            {
                _surface = ms;
                batchLayoutCtrl1.Init(ms);
                _dataContext = ms.GetDataContext();
                batchesBindingSource.CurrentChanged += new EventHandler(batchesBindingSource_CurrentChanged);
                ApplySecurity();
                _initialized = true;
            }



        }

        internal void InvalidateDataContext(bool restorePosition = true)
        {
            batchLayoutCtrl1.InvalidateDataContext();
            int batchPosition = batchesBindingSource.Position;
            var batchSortCol = batchDataGridView.SortedColumn;
            var batchSortOrder = batchDataGridView.SortOrder;

            BindBatches();

            //restore sorting and positon
            if (batchSortCol != null)
            {
                switch (batchSortOrder)
                {
                    case SortOrder.Ascending:
                        {
                            batchDataGridView.Sort(batchSortCol, ListSortDirection.Ascending);
                            break;
                        }

                    case SortOrder.Descending:
                        {
                            batchDataGridView.Sort(batchSortCol, ListSortDirection.Descending);
                            break;
                        }
                    default:
                        break;
                }
            }
            if (restorePosition && batchPosition > -1)
                batchesBindingSource.Position = batchPosition;
        }


        public void NavigateToBatch(string filterTerm, BatchView batch)
        {
            filterBatchesTextBox.TextChanged -= new EventHandler(OnFilterBatches);
            filterBatchesTextBox.Text = filterTerm;
            filterBatchesTextBox.TextChanged += new EventHandler(OnFilterBatches);


            InvalidateDataContext(false);
            int position = batchesBindingSource.IndexOf(batch);
            batchesBindingSource.Position = position;

        }




        #region UI Behavior
        void searchTimer_Tick(object sender, EventArgs e)
        {
            searchTimer.Stop(); //stop after one tick
            BindBatches();
        }

        void kryptonContextMenuRadioButtonFull_CheckedChanged(object sender, EventArgs e)
        {
            if (kryptonContextMenuRadioButtonSplit.Checked)
                kryptonDropButton1.Values.ExtraText = "SPLIT";
            else
                kryptonDropButton1.Values.ExtraText = "FULL";
        }

        void kryptonContextMenuRadioButtonSplit_CheckedChanged(object sender, EventArgs e)
        {
            if (kryptonContextMenuRadioButtonSplit.Checked)
                kryptonDropButton1.Values.ExtraText = "SPLIT";
            else
                kryptonDropButton1.Values.ExtraText = "FULL";
        }
        #endregion

        #region BINDING
        //filter batches
        private void OnFilterBatches(object sender, EventArgs e)
        {
            searchTimer.Interval = 500;
            searchTimer.Stop();
            searchTimer.Start();
            // BindBatches();
        }

        private void BindBatches()
        {
            var q = from x in _dataContext.BatchViews select x;
            if (!string.IsNullOrEmpty(filterBatchesTextBox.Text))
            {

                string[] filterParts = filterBatchesTextBox.Text.Split("-".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                StringBuilder sb = new StringBuilder();
                switch (filterParts.Length)
                {
                    case 1:
                        {
                            int number;
                            DateTime date;

                            if (DateTime.TryParse(filterParts[0], out date) && date.Date > _minDate && date.Date < _maxDate)
                            {
                                q = from x in q where x.Date == date.Date select x;
                            }
                            else if (Int32.TryParse(filterParts[0], out number))
                            {
                                q = from x in q where x.Number == number select x;
                            }
                            else
                            {
                                q = from x in q where x.Name.ToLower().Contains(filterParts[0].ToLower()) select x;
                            }
                            break;
                        }
                    case 2:
                        {
                            int number;
                            if (Int32.TryParse(filterParts[1], out number))
                                q = from x in q
                                    where x.Name.ToLower().Contains(filterParts[0].ToLower())
                                    where x.Number == number
                                    select x;
                            else
                                goto case 1;
                            break;
                        }
                    case 3:
                        {
                            DateTime date;
                            if (DateTime.TryParse(filterParts[2], out date))
                            {
                                int number;
                                if (Int32.TryParse(filterParts[1], out number))
                                    q = from x in q
                                        where x.Name.ToLower().Contains(filterParts[0].ToLower())
                                        where x.Number == number
                                        where x.Date == date.Date
                                        select x;
                                else
                                    goto case 1;
                                break;

                            }
                            else
                                goto case 2;
                        }
                    default:
                        break;
                }
            }
            batchesBindingSource.DataSource = from x in q orderby x.Name, x.Date, x.Number select x;
        }

        private void batchesBindingSource_CurrentChanged(object sender, EventArgs e)
        {
            //LoadBatchLayout();
            navigationTimer.Interval = 350;
            navigationTimer.Stop();
            navigationTimer.Start();
        }

        private void LoadBatchLayout()
        {
            BatchView bv = batchesBindingSource.Current as BatchView;
            this.batchLayoutCtrl1.NavigateBatchLayout(bv);
        }


        #endregion

        #region SECURITY
        private void ApplySecurity()
        {
            batchDataGridView.AllowUserToDeleteRows = false;
            batchDataGridView.ReadOnly = true;
            batchesBindingSource.AllowNew = false;
            batchDeleteItem.Enabled = false;

            if (_surface.SecDescriptor.Rights.ContainsKey("Batches"))
            {
                foreach (TableRight tr in _surface.SecDescriptor.Rights["Batches"])
                {
                    switch (tr)
                    {
                        case TableRight.Insert:
                            {

                                batchesBindingSource.AllowNew = true;
                                break;
                            }
                        case TableRight.Delete:
                            {
                                batchDataGridView.AllowUserToDeleteRows = true;
                                batchDeleteItem.Enabled = true;
                                break;
                            }
                        case TableRight.Update:
                            {
                                batchDataGridView.ReadOnly = false;
                                break;
                            }
                    }
                }
            }



        }

        #endregion

        #region CRUD Batches

        //toolbar initiated add-action
        private void OnAddBatch(object sender, EventArgs e)
        {
            batchesBindingSource.AddNew();
            batchDataGridView.BeginEdit(true);
        }

        //create a default Enzyme for adding
        private void batchesBindingSource_AddingNew(object sender, AddingNewEventArgs e)
        {
            if (kryptonContextMenuRadioButtonSplit.Checked)
                e.NewObject = new BatchView { UId = _newItemUId, Name = "NewLayout", ColumnCount = 12, RowCount = 8, Number = _nextBatchNumber, Date = DateTime.Now.Date, LayoutType = "SPLIT", ProtocolName = "~Default" };
            else
                e.NewObject = new BatchView { UId = _newItemUId, Name = "NewLayout", ColumnCount = 12, RowCount = 8, Number = _nextBatchNumber, Date = DateTime.Now.Date, LayoutType = "FULL", ProtocolName = "~Default" };

        }

        //validate added and updated batches
        private void batchDataGridView_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
        {
            if (batchDataGridView.IsCurrentCellInEditMode)
            {
                DataGridViewRow row = batchDataGridView.Rows[e.RowIndex];
                DataGridViewColumn col = batchDataGridView.Columns[e.ColumnIndex];
                var mv = row.DataBoundItem as BatchView;

                string val = e.FormattedValue.ToString();
                if (mv == null)
                {
                    return;
                }

                if (col.DataPropertyName == "Name")
                {
                    if (string.IsNullOrEmpty(val))
                    {
                        batchDataGridView.Rows[e.RowIndex].ErrorText = "The batch name can't be empty.";
                        e.Cancel = true;
                        return;
                    }
                    if (CausesDoubleEntry(mv.UId, val, mv.Number, mv.Date))
                    {
                        batchDataGridView.Rows[e.RowIndex].ErrorText = "A batch with this name, number and date already exists.";
                        e.Cancel = true;
                        return;
                    }
                }
                else if (col.DataPropertyName == "Number")
                {
                    int number;
                    bool isInt = int.TryParse(val, out number);
                    if (!isInt)
                    {
                        batchDataGridView.Rows[e.RowIndex].ErrorText = "The batch number must be a valid number";
                        e.Cancel = true;
                        return;
                    }
                    if (CausesDoubleEntry(mv.UId, mv.Name, number, mv.Date))
                    {
                        batchDataGridView.Rows[e.RowIndex].ErrorText = "A batch with this name, number and date already exists.";
                        e.Cancel = true;
                        return;
                    }
                }
                else if (col.DataPropertyName == "Date")
                {
                    DateTime date;
                    bool isDateTime = DateTime.TryParse(val, out date);
                    if (!isDateTime || date.Date < _minDate || date.Date > _maxDate)
                    {
                        batchDataGridView.Rows[e.RowIndex].ErrorText = "The batch date must be a valid date";
                        e.Cancel = true;
                        return;
                    }
                    if (CausesDoubleEntry(mv.UId, mv.Name, mv.Number, date.Date))
                    {
                        batchDataGridView.Rows[e.RowIndex].ErrorText = "A batch with this name, number and date already exists.";
                        e.Cancel = true;
                        return;
                    }
                }
                else if (col.DataPropertyName == "ProtocolName")
                {
                    using (EnzymeDBDataContext context = _surface.GetDataContext())
                    {
                        if (context.ProtocolViews.SingleOrDefault(x => x.Name == val) == null)
                        {
                            batchDataGridView.Rows[e.RowIndex].ErrorText = "Unknown protocol.";
                            e.Cancel = true;
                            return;
                        }
                        var raw = context.RawDatas.FirstOrDefault(x => x.BatchName_Fk == mv.Name && x.BatchNumber_Fk == mv.Number && x.BatchDate_Fk == mv.Date);
                        if (raw != null)
                        {

                            batchDataGridView.Rows[e.RowIndex].ErrorText = "There are already raw data associated with this batch. Therefore the protocol can't be changed.\r\nPress escape to return to original protocol.";
                            e.Cancel = true;
                            return;
                        }
                    }
                }


            }
        }

        //save added and updated batches
        private void batchDataGridView_CellEndEdit(object sender, DataGridViewCellEventArgs e)
        {
            batchDataGridView.Rows[e.RowIndex].ErrorText = null;
            BatchView mv = null;
            try
            {
                mv = batchDataGridView.Rows[e.RowIndex].DataBoundItem as BatchView;
                if (mv != null)
                {
                    if (mv.UId == _newItemUId)
                    {
                        mv.UId = _surface.AddBatch(mv.Name, mv.Number, mv.Date, mv.ProtocolName, mv.LayoutType, mv.RowCount, mv.ColumnCount);
                        _nextBatchNumber++; //increase batchnumber after succesfull add for the next batch
                    }
                    else
                    {
                        _surface.UpdateBatch(mv.UId, mv.Name, mv.Number, mv.Date, mv.ProtocolName);
                    }
                }
            }
            catch (Exception ex)
            {
                Microsoft.SqlServer.MessageBox.ExceptionMessageBox box = new Microsoft.SqlServer.MessageBox.ExceptionMessageBox(ex);
                box.Show(this);
            }
        }

        //toolbar initiated delete-action
        private void batchDeleteItem_Click(object sender, EventArgs e)
        {
            if (ConfirmBatchDelete())
            {
                BatchView mv = batchesBindingSource.Current as BatchView;
                if (mv != null)
                {
                    _surface.DeletBatch(mv.UId);
                    batchesBindingSource.RemoveCurrent();
                }
            }
        }

        //gridview initiated delete-action
        private void batchDataGridView_UserDeletingRow(object sender, DataGridViewRowCancelEventArgs e)
        {
            e.Cancel = !ConfirmBatchDelete();
        }

        //delete batch 
        private void batchDataGridView_UserDeletedRow(object sender, DataGridViewRowEventArgs e)
        {
            var mv = e.Row.DataBoundItem as BatchView;
            if (mv != null)
            {
                _surface.DeletBatch(mv.UId);
            }
        }

        //confirms the delete of an batch
        private bool ConfirmBatchDelete()
        {
            return MessageBox.Show("Are you sure you want to delete the current batch?\r\nThis action will also delete all corresponding raw data!", "Confirm batch delete", MessageBoxButtons.YesNo, MessageBoxIcon.Question)
                == DialogResult.Yes;
        }

        //checks whether a given set of parameter would cause a double entry in batches
        private bool CausesDoubleEntry(Guid guid, string name, int number, DateTime date)
        {
            var q = from x in _dataContext.BatchViews
                    where x.UId != guid
                    where x.Name == name
                    where x.Number == number
                    where x.Date == date
                    select x;
            if (q.Count() > 0)
                return true;
            else
                return false;
        }
        #endregion





        internal bool AllowNavigation()
        {
            if (!batchLayoutCtrl1.UnSavedChanges)
                return true;
            else
            {
                return RequestToNavigateAway(true);
            }
        }

        private void batchDataGridView_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
        {
            DataGridViewColumn dgv = batchDataGridView.Columns[batchDataGridView.CurrentCell.ColumnIndex];
            if (dgv.DataPropertyName == "ProtocolName")
            {
                TextBox tb = e.Control as TextBox;
                tb.AutoCompleteMode = AutoCompleteMode.Suggest;
                tb.AutoCompleteSource = AutoCompleteSource.CustomSource;
                using (EnzymeDBDataContext context = _surface.GetDataContext())
                {
                    var units = (from x in context.Protocols orderby x.Name select x.Name).ToArray();
                    tb.AutoCompleteCustomSource.Clear();
                    tb.AutoCompleteCustomSource.AddRange(units);
                }
            }
        }

        private void BatchCtrl_Load(object sender, EventArgs e)
        {
            batchLayoutCtrl1.StatusChanged += new EventHandler<StatusChangedEventArgs>(batchLayoutCtrl1_StatusChanged);
        }

        void batchLayoutCtrl1_StatusChanged(object sender, StatusChangedEventArgs e)
        {
            if (this.StatusChanged != null)
            {
                StatusChanged(this, e);
            }
        }

        public event EventHandler<StatusChangedEventArgs> StatusChanged;

    }
}
